#pragma once

#include <iostream>
#include <memory>
#include "Socket.hpp"
#include "Reactor.hpp"
#include "Log.hpp"
#include "Connection.hpp"
#include "Protocol.hpp"

/*
多路转接对写的处理：直接写，写入默认是就绪的，写入失败，才托管给epoll。
多路转接方案设计的时候，写事件关心，永远不能长开启，需要按需设置。

读事件一般常设，写事件一般按需。

*/

using func_t = std::function<std::string(std::string&)>;

// 只负责IO
class IOService : public Connection
{
    static const int size = 1024;
public:
    IOService(int sockfd)
    {
        SetSockfd(sockfd);
        SetEvents(EPOLLIN | EPOLLET);
    }

    virtual void Recver() override
    {
        // ET模式下，循环读取
        while (true)
        {
            char buffer[size];
            ssize_t n = recv(Sockfd(), buffer, sizeof(buffer) - 1, 0);
            if (n > 0)
            {
                buffer[n] = 0;
                // 添加到特定文件描述符的缓冲区
                Append2In(buffer);
            }
            else if (n == 0)
            {
                // 对端关闭连接
                Excepter();
                return;
            }
            else
            {
                if (errno == EAGAIN || errno == EWOULDBLOCK)
                {
                    break;
                }
                else if (errno == EINTR) continue;
                else
                {
                    Excepter();
                    return;
                }
            }
        }
        // 走到这里一定能保证把数据读完了
        // 但是还不能保证读到报文的完整性，需要引入协议！
        std::string result;
        if (_on_message) 
            result = _on_message(InBuffer());
        
        // 添加应答信息
        Append2Out(result);

        // 将outbuffer发送给对方
        if (!IsOutBufferEmpty()) Sender();
    }

    void RegisterOnMessage(func_t on_message)
    {
        _on_message = on_message;
    }

    virtual void Sender() override
    {
        while (true)
        {
            ssize_t n = send(Sockfd(), OutBuffer().c_str(), OutBuffer().size(), 0);
            if (n > 0)
            {
                // 移除发送过的n个字节
                DisCardOutBuffer(n);
            }
            else if (n == 0) break;
            else
            {
                if (errno == EAGAIN || errno == EWOULDBLOCK)
                {
                    // 缓冲区写满了
                    break;
                }
                else if (errno == EINTR) continue;
                else
                {
                    Excepter();
                    return;
                }
            }
        }

        // 1.outbuffer为空了
        // 2.发送缓冲区被写满了 && outbuffer不为空
        if (!IsOutBufferEmpty())
        {
            // 修改对文件描述符的事件关心
            GetOwner()->EnableReadWrite(Sockfd(), true, true);
        }
        else
        {
            GetOwner()->EnableReadWrite(Sockfd(), true, false);
        }
    }

    // IO读取的时候，所有的异常处理，都会转换位这个函数的调用
    virtual void Excepter() override
    {
        // 关闭连接、从内核中移除对文件描述符的关心
        LOG(LogLevel::INFO) << "客户端连接可能结束，进行异常处理: " << Sockfd();
        GetOwner()->DelConnection(Sockfd());
    }
private:
    func_t _on_message;
};